/*     */ package com.zimbra.cs.db;
/*     */ 
/*     */ import com.google.common.base.Joiner;
/*     */ import com.google.common.base.Strings;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.redolog.Version;
/*     */ import java.io.BufferedWriter;
/*     */ import java.io.File;
/*     */ import java.io.FileWriter;
/*     */ import java.io.IOException;
/*     */ import java.io.PrintStream;
/*     */ import java.io.Writer;
/*     */ import java.sql.Connection;
/*     */ import java.sql.PreparedStatement;
/*     */ import java.sql.ResultSet;
/*     */ import java.sql.SQLException;
/*     */ import java.util.HashMap;
/*     */ import java.util.Iterator;
/*     */ import java.util.LinkedHashMap;
/*     */ import java.util.Map;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Properties;
/*     */ import java.util.Set;
/*     */ import java.util.concurrent.ConcurrentHashMap;
/*     */ import java.util.concurrent.ConcurrentMap;
/*     */ import java.util.concurrent.TimeUnit;
/*     */ import java.util.concurrent.locks.ReentrantLock;
/*     */ import org.apache.commons.cli.CommandLine;
/*     */ import org.apache.commons.cli.Options;
/*     */ import org.apache.commons.dbcp.DelegatingConnection;
/*     */ import org.apache.commons.dbcp.PoolingDataSource;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public final class SQLite
/*     */   extends Db
/*     */ {
/*     */   private static final String PRAGMA_JOURNAL_MODE_DEFAULT = "DELETE";
/*     */   private static final String PRAGMA_SYNCHRONOUS_DEFAULT = "FULL";
/*     */   private Map<Db.Error, String> mErrorCodes;
/*     */   private String cacheSize;
/*     */   private String journalMode;
/*     */   private String pageSize;
/*     */   private String syncMode;
/*     */   private static final int DEFAULT_CONNECTION_POOL_SIZE = 6;
/*     */   
/*     */   SQLite()
/*     */   {
/*  64 */     this.mErrorCodes = new HashMap(6);
/*  65 */     this.mErrorCodes.put(Db.Error.DUPLICATE_ROW, "not unique");
/*  66 */     this.mErrorCodes.put(Db.Error.NO_SUCH_TABLE, "no such table");
/*  67 */     this.mErrorCodes.put(Db.Error.FOREIGN_KEY_CHILD_EXISTS, "foreign key");
/*  68 */     this.mErrorCodes.put(Db.Error.FOREIGN_KEY_NO_PARENT, "foreign key");
/*  69 */     this.mErrorCodes.put(Db.Error.TOO_MANY_SQL_PARAMS, "too many SQL variables");
/*  70 */     this.mErrorCodes.put(Db.Error.BUSY, "SQLITE_BUSY");
/*  71 */     this.mErrorCodes.put(Db.Error.LOCKED, "database is locked");
/*  72 */     this.mErrorCodes.put(Db.Error.CANTOPEN, "SQLITE_CANTOPEN");
/*     */   }
/*     */   
/*     */   boolean supportsCapability(Db.Capability capability)
/*     */   {
/*  77 */     switch (capability) {
/*  78 */     case AVOID_OR_IN_WHERE_CLAUSE:  return false;
/*  79 */     case BITWISE_OPERATIONS:  return true;
/*  80 */     case BOOLEAN_DATATYPE:  return false;
/*  81 */     case CASE_SENSITIVE_COMPARISON:  return true;
/*  82 */     case CAST_AS_BIGINT:  return false;
/*  83 */     case CLOB_COMPARISON:  return true;
/*  84 */     case DISABLE_CONSTRAINT_CHECK:  return false;
/*  85 */     case FILE_PER_DATABASE:  return true;
/*  86 */     case FORCE_INDEX_EVEN_IF_NO_SORT:  return false;
/*  87 */     case LIMIT_CLAUSE:  return true;
/*  88 */     case MULTITABLE_UPDATE:  return false;
/*  89 */     case NON_BMP_CHARACTERS:  return true;
/*  90 */     case ON_DUPLICATE_KEY:  return false;
/*  91 */     case ON_UPDATE_CASCADE:  return true;
/*  92 */     case READ_COMMITTED_ISOLATION:  return false;
/*  93 */     case REPLACE_INTO:  return true;
/*  94 */     case REQUEST_UTF8_UNICODE_COLLATION:  return false;
/*  95 */     case ROW_LEVEL_LOCKING:  return false;
/*  96 */     case UNIQUE_NAME_INDEX:  return false;
/*  97 */     case SQL_PARAM_LIMIT:  return true;
/*  98 */     case DUMPSTER_TABLES:  return false;
/*     */     }
/* 100 */     return false;
/*     */   }
/*     */   
/*     */ 
/*     */   boolean compareError(SQLException e, Db.Error error)
/*     */   {
/* 106 */     String code = (String)this.mErrorCodes.get(error);
/* 107 */     return (code != null) && (e.getMessage() != null) && (e.getMessage().contains(code));
/*     */   }
/*     */   
/*     */ 
/*     */   String forceIndexClause(String index)
/*     */   {
/* 113 */     return "";
/*     */   }
/*     */   
/*     */   String getIFNULLClause(String expr1, String expr2)
/*     */   {
/* 118 */     return "IFNULL(" + expr1 + ", " + expr2 + ")";
/*     */   }
/*     */   
/*     */   public String bitAND(String expr1, String expr2)
/*     */   {
/* 123 */     return expr1 + " & " + expr2;
/*     */   }
/*     */   
/*     */   public String bitANDNOT(String expr1, String expr2)
/*     */   {
/* 128 */     return expr1 + " & ~" + expr2;
/*     */   }
/*     */   
/*     */   DbPool.PoolConfig getPoolConfig()
/*     */   {
/* 133 */     return new SQLiteConfig();
/*     */   }
/*     */   
/*     */   void startup(PoolingDataSource pool, int poolSize)
/*     */     throws SQLException
/*     */   {
/* 139 */     this.cacheSize = LC.sqlite_cache_size.value();
/* 140 */     if (this.cacheSize.equals("0"))
/* 141 */       this.cacheSize = null;
/* 142 */     this.journalMode = LC.sqlite_journal_mode.value();
/* 143 */     this.pageSize = LC.sqlite_page_size.value();
/* 144 */     if (this.pageSize.equals("0"))
/* 145 */       this.pageSize = null;
/* 146 */     this.syncMode = LC.sqlite_sync_mode.value();
/* 147 */     ZimbraLog.dbconn.info("sqlite driver running with " + (this.cacheSize == null ? "default" : this.cacheSize) + " cache cache, " + (this.pageSize == null ? "default" : this.pageSize) + " page size, " + this.journalMode + " journal mode, " + this.syncMode + " sync mode");
/*     */     
/*     */ 
/*     */ 
/* 151 */     super.startup(pool, poolSize);
/*     */   }
/*     */   
/*     */   void postCreate(Connection conn) throws SQLException
/*     */   {
/*     */     try {
/* 157 */       conn.setAutoCommit(true);
/* 158 */       pragmas(conn, null);
/*     */     } finally {
/* 160 */       conn.setAutoCommit(false);
/*     */     }
/*     */   }
/*     */   
/*     */   private void pragma(Connection conn, String dbname, String key, String value) throws SQLException {
/* 165 */     PreparedStatement stmt = null;
/*     */     try
/*     */     {
/* 168 */       String prefix = dbname + ".";
/* 169 */       (stmt = conn.prepareStatement("PRAGMA " + prefix + key + (value == null ? "" : new StringBuilder().append(" = ").append(value).toString()))).execute();
/*     */     }
/*     */     finally {
/* 172 */       DbPool.quietCloseStatement(stmt);
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   void pragmas(Connection conn, String dbname)
/*     */     throws SQLException
/*     */   {
/* 181 */     pragma(conn, dbname, "foreign_keys", "ON");
/* 182 */     if ((this.journalMode != null) && (!this.journalMode.equalsIgnoreCase("DELETE")))
/* 183 */       pragma(conn, dbname, "journal_mode", this.journalMode);
/* 184 */     if ((this.syncMode != null) && (!this.syncMode.equalsIgnoreCase("FULL")))
/* 185 */       pragma(conn, dbname, "synchronous", this.syncMode);
/* 186 */     if (this.cacheSize != null)
/* 187 */       pragma(conn, dbname, "cache_size", this.cacheSize);
/* 188 */     if (this.pageSize != null) {
/* 189 */       pragma(conn, dbname, "page_size", this.pageSize);
/*     */     }
/*     */   }
/*     */   
/*     */ 
/* 194 */   private static final int MAX_ATTACHED_DATABASES = readConfigInt("sqlite_max_attached_databases", "max # of attached databases", 7);
/*     */   
/* 196 */   private static final HashMap<Connection, LinkedHashMap<String, String>> sAttachedDatabases = new HashMap(6);
/*     */   
/*     */   private LinkedHashMap<String, String> getAttachedDatabases(DbPool.DbConnection conn)
/*     */   {
/* 200 */     return (LinkedHashMap)sAttachedDatabases.get(getInnermostConnection(conn.getConnection()));
/*     */   }
/*     */   
/*     */   private Connection getInnermostConnection(Connection conn) {
/* 204 */     Connection retVal = null;
/* 205 */     if ((conn instanceof DebugConnection))
/* 206 */       retVal = ((DebugConnection)conn).getConnection();
/* 207 */     if ((conn instanceof DelegatingConnection))
/* 208 */       retVal = ((DelegatingConnection)conn).getInnermostDelegate();
/* 209 */     return retVal == null ? conn : retVal;
/*     */   }
/*     */   
/*     */   public void optimize(DbPool.DbConnection conn, String dbname, int level) throws ServiceException
/*     */   {
/*     */     try
/*     */     {
/* 216 */       boolean autocommit = conn.getConnection().getAutoCommit();
/* 217 */       PreparedStatement stmt = null;
/*     */       try
/*     */       {
/* 220 */         if (!autocommit)
/* 221 */           conn.getConnection().setAutoCommit(true);
/* 222 */         if (dbname == null)
/* 223 */           dbname = "zimbra";
/* 224 */         registerDatabaseInterest(conn, dbname);
/* 225 */         if ((level > 0) && (dbname.endsWith("zimbra"))) {
/* 226 */           if (level == 2) {
/* 227 */             (stmt = conn.prepareStatement("VACUUM")).execute();
/*     */           } else
/* 229 */             pragma(conn.getConnection(), dbname, "incremental_vacuum", null);
/*     */         }
/* 231 */         (stmt = conn.prepareStatement("ANALYZE " + dbname)).execute();
/* 232 */         ZimbraLog.dbconn.debug("sqlite " + (level > 0 ? "vacuum" : "analyze") + ' ' + dbname);
/*     */       }
/*     */       finally {
/* 235 */         if (!autocommit) {
/*     */           try {
/* 237 */             conn.getConnection().setAutoCommit(autocommit);
/*     */           } catch (SQLException sqle) {
/* 239 */             ZimbraLog.dbconn.warn("failed to reset autocommit to false. probably caused by prior errors %s", new Object[] { dbname });
/* 240 */             DbPool.quietClose(conn);
/* 241 */             throw ServiceException.FAILURE("failed to reset autocommit to false", sqle);
/*     */           }
/*     */         }
/*     */         
/* 245 */         DbPool.quietCloseStatement(stmt);
/*     */       }
/*     */     } catch (Exception e) {
/* 248 */       throw ServiceException.FAILURE("sqlite " + (level > 0 ? "vacuum" : "analyze") + ' ' + dbname + " error", e);
/*     */     }
/*     */   }
/*     */   
/*     */   public void registerDatabaseInterest(DbPool.DbConnection conn, String dbname)
/*     */     throws SQLException, ServiceException
/*     */   {
/* 255 */     LinkedHashMap<String, String> attachedDBs = getAttachedDatabases(conn);
/* 256 */     if ((attachedDBs != null) && (attachedDBs.containsKey(dbname))) {
/*     */       return;
/*     */     }
/*     */     Iterator<String> it;
/* 260 */     if ((attachedDBs != null) && (attachedDBs.size() >= MAX_ATTACHED_DATABASES)) {
/* 261 */       for (it = attachedDBs.keySet().iterator(); (attachedDBs.size() >= MAX_ATTACHED_DATABASES) && (it.hasNext());) {
/* 262 */         String name = (String)it.next();
/*     */         
/* 264 */         if ((!name.equals("zimbra")) && (detachDatabase(conn, name)))
/* 265 */           it.remove();
/*     */       }
/*     */     }
/* 268 */     attachDatabase(conn, dbname);
/*     */   }
/*     */   
/*     */   void attachDatabase(DbPool.DbConnection conn, String dbname) throws SQLException, ServiceException {
/* 272 */     PreparedStatement stmt = null;
/* 273 */     boolean autocommit = true;
/*     */     try {
/* 275 */       autocommit = conn.getConnection().getAutoCommit();
/* 276 */       if (!autocommit) {
/* 277 */         conn.getConnection().setAutoCommit(true);
/*     */       }
/* 279 */       (stmt = conn.prepareStatement("ATTACH DATABASE \"" + getDatabaseFilename(dbname) + "\" AS " + dbname)).execute();
/* 280 */       pragmas(conn.getConnection(), dbname);
/*     */     } catch (SQLException e) {
/* 282 */       ZimbraLog.dbconn.error("database " + dbname + " attach failed", e);
/* 283 */       if (!"database is already attached".equals(e.getMessage()))
/* 284 */         throw e;
/*     */     } finally {
/* 286 */       if (!autocommit) {
/*     */         try {
/* 288 */           conn.getConnection().setAutoCommit(autocommit);
/*     */         } catch (SQLException sqle) {
/* 290 */           ZimbraLog.dbconn.warn("failed to reset autocommit to false. probably caused by prior errors " + dbname);
/* 291 */           DbPool.quietClose(conn);
/* 292 */           throw ServiceException.FAILURE("failed to reset autocommit to false", sqle);
/*     */         }
/*     */       }
/* 295 */       DbPool.quietCloseStatement(stmt);
/*     */     }
/*     */     
/* 298 */     LinkedHashMap<String, String> attachedDBs = getAttachedDatabases(conn);
/* 299 */     if (attachedDBs != null) {
/* 300 */       attachedDBs.put(dbname, null);
/*     */     } else {
/* 302 */       attachedDBs = new LinkedHashMap(MAX_ATTACHED_DATABASES * 3 / 2, 0.75F, true);
/* 303 */       attachedDBs.put(dbname, null);
/* 304 */       sAttachedDatabases.put(getInnermostConnection(conn.getConnection()), attachedDBs);
/*     */     }
/*     */   }
/*     */   
/*     */   private boolean detachDatabase(DbPool.DbConnection conn, String dbname) throws ServiceException {
/* 309 */     PreparedStatement stmt = null;
/* 310 */     boolean autocommit = true;
/*     */     try {
/* 312 */       autocommit = conn.getConnection().getAutoCommit();
/* 313 */       if (!autocommit) {
/* 314 */         conn.getConnection().setAutoCommit(true);
/*     */       }
/* 316 */       (stmt = conn.prepareStatement("DETACH DATABASE " + dbname)).execute();
/* 317 */       return true;
/*     */     } catch (SQLException e) {
/* 319 */       if (!this.deleted.containsKey(dbname)) {
/* 320 */         ZimbraLog.dbconn.warn("database overflow autoclose failed for DB " + dbname, e);
/* 321 */         return 0;
/*     */       }
/* 323 */       return 1;
/*     */     }
/*     */     finally {
/* 326 */       if (!autocommit) {
/*     */         try {
/* 328 */           conn.getConnection().setAutoCommit(autocommit);
/*     */         } catch (SQLException sqle) {
/* 330 */           ZimbraLog.dbconn.warn("failed to reset autocommit to false. probably caused by prior errors %s", new Object[] { dbname });
/* 331 */           DbPool.quietClose(conn);
/* 332 */           throw ServiceException.FAILURE("failed to reset autocommit to false", sqle);
/*     */         }
/*     */       }
/* 335 */       DbPool.quietCloseStatement(stmt);
/*     */     }
/*     */   }
/*     */   
/*     */   private void releaseMboxDbLock(Integer mboxId) {
/* 340 */     if (mboxId != null) {
/* 341 */       ReentrantLock lock = null;
/* 342 */       lock = (ReentrantLock)lockMap.get(mboxId);
/* 343 */       if ((lock != null) && (lock.isHeldByCurrentThread())) {
/* 344 */         lock.unlock();
/* 345 */         ZimbraLog.dbconn.trace("unlocked mbox %d", new Object[] { mboxId });
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   void preClose(DbPool.DbConnection conn)
/*     */   {
/* 352 */     releaseMboxDbLock(conn.mboxId);
/*     */   }
/*     */   
/*     */ 
/* 356 */   private static ConcurrentMap<Integer, ReentrantLock> lockMap = new ConcurrentHashMap();
/*     */   
/*     */   private boolean checkLockMap(int mboxId) {
/* 359 */     for (Map.Entry<Integer, ReentrantLock> entry : lockMap.entrySet()) {
/* 360 */       if ((((Integer)entry.getKey()).intValue() != mboxId) && (((ReentrantLock)entry.getValue()).isHeldByCurrentThread())) {
/* 361 */         ZimbraLog.dbconn.debug("already holding db lock for mbox %d", new Object[] { entry.getKey() });
/* 362 */         if (((Integer)entry.getKey()).intValue() != -1) {
/* 363 */           return false;
/*     */         }
/*     */       }
/*     */     }
/* 367 */     return true;
/*     */   }
/*     */   
/*     */   void preOpen(Integer mboxId)
/*     */   {
/* 372 */     ZimbraLog.dbconn.trace("trying to lock mbox %d", new Object[] { mboxId });
/* 373 */     assert (checkLockMap(mboxId.intValue()));
/* 374 */     ReentrantLock lock = (ReentrantLock)lockMap.get(mboxId);
/* 375 */     if (lock == null) {
/* 376 */       lock = new ReentrantLock();
/* 377 */       ReentrantLock added = (ReentrantLock)lockMap.putIfAbsent(mboxId, lock);
/* 378 */       if (added != null) {
/* 379 */         lock = added;
/*     */       }
/*     */     }
/* 382 */     boolean locked = false;
/* 383 */     long timeoutSecs = 180L;
/*     */     
/*     */ 
/*     */     try
/*     */     {
/* 388 */       locked = lock.tryLock(timeoutSecs, TimeUnit.SECONDS);
/*     */     }
/*     */     catch (InterruptedException e) {}
/* 391 */     if (!locked) {
/* 392 */       ZimbraLog.dbconn.warn("Unable to get db lock for mbox %d", new Object[] { mboxId });
/*     */     } else {
/* 394 */       ZimbraLog.dbconn.trace("locked mbox %d", new Object[] { mboxId });
/*     */     }
/*     */   }
/*     */   
/*     */   void abortOpen(Integer mboxId)
/*     */   {
/* 400 */     releaseMboxDbLock(mboxId);
/*     */   }
/*     */   
/*     */   public boolean databaseExists(DbPool.DbConnection conn, String dbname) throws ServiceException
/*     */   {
/* 405 */     if (!new File(getDatabaseFilename(dbname)).exists()) {
/* 406 */       return false;
/*     */     }
/*     */     
/*     */ 
/* 410 */     PreparedStatement stmt = null;
/* 411 */     ResultSet rs = null;
/* 412 */     boolean autocommit = true;
/*     */     try {
/* 414 */       autocommit = conn.getConnection().getAutoCommit();
/* 415 */       if (!autocommit) {
/* 416 */         conn.getConnection().setAutoCommit(true);
/*     */       }
/* 418 */       registerDatabaseInterest(conn, dbname);
/* 419 */       stmt = conn.prepareStatement("SELECT COUNT(*) FROM " + (dbname.equals("zimbra") ? "" : new StringBuilder().append(dbname).append(".").toString()) + "sqlite_master WHERE type='table'");
/*     */       
/*     */ 
/* 422 */       rs = stmt.executeQuery();
/* 423 */       boolean complete = rs.getInt(1) >= 1;
/* 424 */       return complete;
/*     */     } catch (SQLException e) {
/* 426 */       throw ServiceException.FAILURE("sqlite error", e);
/*     */     } finally {
/* 428 */       if (!autocommit) {
/*     */         try {
/* 430 */           conn.getConnection().setAutoCommit(autocommit);
/*     */         } catch (SQLException sqle) {
/* 432 */           ZimbraLog.dbconn.warn("failed to reset autocommit to false. probably caused by prior errors %s", new Object[] { dbname });
/* 433 */           DbPool.quietClose(conn);
/* 434 */           throw ServiceException.FAILURE("failed to reset autocommit to false", sqle);
/*     */         }
/*     */       }
/* 437 */       DbPool.closeResults(rs);
/* 438 */       DbPool.closeStatement(stmt);
/*     */     }
/*     */   }
/*     */   
/* 442 */   private ConcurrentMap<String, Boolean> deleted = new ConcurrentHashMap();
/*     */   
/*     */   void deleteDatabaseFile(DbPool.DbConnection conn, String dbname)
/*     */   {
/* 446 */     assert ((dbname != null) && (!dbname.trim().equals("")));
/*     */     try {
/* 448 */       detachDatabase(conn, dbname);
/*     */     } catch (ServiceException se) {
/* 450 */       ZimbraLog.dbconn.warn("failed to detach while deleting");
/*     */     }
/* 452 */     this.deleted.put(dbname, Boolean.valueOf(true));
/* 453 */     ZimbraLog.dbconn.info("deleting database file for DB '" + dbname + "'");
/* 454 */     new File(getDatabaseFilename(dbname)).delete();
/* 455 */     new File(getDatabaseFilename(dbname) + "-journal").delete();
/*     */   }
/*     */   
/*     */   public String getDatabaseFilename(String dbname)
/*     */   {
/* 460 */     return LC.zimbra_home.value() + File.separator + "sqlite" + File.separator + dbname + ".db";
/*     */   }
/*     */   
/*     */   final class SQLiteConfig extends DbPool.PoolConfig {
/*     */     SQLiteConfig() {
/* 465 */       this.mDriverClassName = "org.sqlite.JDBC";
/* 466 */       this.mPoolSize = 6;
/* 467 */       this.mRootUrl = null;
/* 468 */       this.mConnectionUrl = ("jdbc:sqlite:" + SQLite.this.getDatabaseFilename("zimbra"));
/* 469 */       this.mLoggerUrl = null;
/* 470 */       this.mSupportsStatsCallback = false;
/* 471 */       this.mDatabaseProperties = getSQLiteProperties();
/* 472 */       this.whenExhaustedAction = 2;
/*     */       
/*     */ 
/* 475 */       this.mPoolSize = SQLite.readConfigInt("sqlite_pool_size", "connection pool size", 6);
/*     */     }
/*     */     
/*     */     private Properties getSQLiteProperties() {
/* 479 */       Properties props = new Properties();
/* 480 */       if (LC.sqlite_shared_cache_enabled.booleanValue())
/* 481 */         props.setProperty("shared_cache", "true");
/* 482 */       return props;
/*     */     }
/*     */   }
/*     */   
/*     */   static int readConfigInt(String keyname, String description, int defaultvalue) {
/* 487 */     int value = defaultvalue;
/*     */     try {
/* 489 */       String configvalue = LC.get(keyname);
/* 490 */       if ((configvalue != null) && (!configvalue.trim().equals("")))
/* 491 */         value = Math.max(1, Integer.parseInt(configvalue));
/*     */     } catch (NumberFormatException nfe) {
/* 493 */       ZimbraLog.dbconn.warn("exception parsing '" + keyname + "' config; defaulting limit to " + defaultvalue, nfe);
/*     */     }
/* 495 */     ZimbraLog.dbconn.info("setting " + description + " to " + value);
/* 496 */     return value;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   public void flushToDisk() {}
/*     */   
/*     */ 
/*     */ 
/*     */   public String toString()
/*     */   {
/* 507 */     return "SQLite";
/*     */   }
/*     */   
/*     */   protected int getInClauseBatchSize()
/*     */   {
/* 512 */     return 200;
/*     */   }
/*     */   
/*     */   public void checkParamLimit(int numParams) throws ServiceException
/*     */   {
/* 517 */     if (numParams > getParamLimit()) {
/* 518 */       throw ServiceException.FAILURE("SQLite parameter limit will be exceeded", new SQLException((String)this.mErrorCodes.get(Db.Error.TOO_MANY_SQL_PARAMS)));
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */   public int getParamLimit()
/*     */   {
/* 525 */     return 999;
/*     */   }
/*     */   
/*     */   public static void main(String[] args)
/*     */   {
/* 530 */     Options options = new Options();
/* 531 */     CommandLine cl = Versions.parseCmdlineArgs(args, options);
/*     */     
/* 533 */     String outputDir = cl.getOptionValue("o");
/* 534 */     File outFile = new File(outputDir, "versions-init.sql");
/* 535 */     outFile.delete();
/*     */     try
/*     */     {
/* 538 */       String redoVer = Version.latest().toString();
/* 539 */       String outStr = "-- AUTO-GENERATED .SQL FILE - Generated by the SQLite versions tool\nINSERT INTO config(name, value, description) VALUES\n\t('db.version', '103', 'db schema version');\nINSERT INTO config(name, value, description) VALUES\n\t('index.version', '2', 'index version');\nINSERT INTO config(name, value, description) VALUES\n\t('redolog.version', '" + redoVer + "', 'redolog version');\n";
/*     */       
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 547 */       Writer output = new BufferedWriter(new FileWriter(outFile));
/* 548 */       output.write(outStr);
/* 549 */       output.close();
/*     */     } catch (IOException e) {
/* 551 */       System.out.println("ERROR - caught exception at\n");
/* 552 */       e.printStackTrace();
/* 553 */       System.exit(-1);
/*     */     }
/*     */   }
/*     */   
/*     */   public String concat(String... fieldsToConcat)
/*     */   {
/* 559 */     Joiner joiner = Joiner.on(" || ").skipNulls();
/* 560 */     return joiner.join(fieldsToConcat);
/*     */   }
/*     */   
/*     */   public String sign(String field)
/*     */   {
/* 565 */     return "CASE WHEN(" + field + ")>0 THEN '1' WHEN(" + field + ")<0 THEN '-1' ELSE '0' END";
/*     */   }
/*     */   
/*     */   public String lpad(String field, int padSize, String padString)
/*     */   {
/* 570 */     return "SUBSTR('" + Strings.repeat(padString, padSize) + "' || " + field + ", -" + padSize + ", " + padSize + ")";
/*     */   }
/*     */   
/*     */   public String limit(int offset, int limit)
/*     */   {
/* 575 */     return "LIMIT " + offset + "," + limit;
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/db/SQLite.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */